﻿using System;
using System.Collections.Generic;
using System.Reflection.Emit;

namespace WoltersKluwer.SPChainGang.Core.Util
{
    public static class FastObjectFactory
    {
        static readonly Dictionary<string, object> cachedDelegates = new Dictionary<string, object>();

        static readonly object lockObject = new object();

        public static object CreateObject<TArgs>(Type type, TArgs constructorArg)
        {
            object constructor;
            var cacheKey = string.Format("{0}_{1}", type.FullName, typeof(TArgs).FullName);
            if (!cachedDelegates.TryGetValue(cacheKey, out constructor))
            {
                constructor = BuildConstructor<TArgs>(type);
                lock (lockObject)
                {
                    if (!cachedDelegates.ContainsKey(cacheKey))
                        cachedDelegates.Add(cacheKey, constructor);
                }
            }
            return ((Func<TArgs, object>)constructor)(constructorArg);
        }

        public static TResult CreateObject<TArgs, TResult>(TArgs constructorArg)
        {
            return (TResult)CreateObject(typeof(TResult), constructorArg);
        }

        static Func<TArgs, object> BuildConstructor<TArgs>(Type type)
        {
            var dynamicMethod = new DynamicMethod(
                string.Format("{0}_DynamicConstructor", type.FullName), type, new[] { typeof(TArgs) }, type.Module);
            var il = dynamicMethod.GetILGenerator();
            var constructor = type.GetConstructor(new[] { typeof(TArgs) });
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Newobj, constructor);
            il.Emit(OpCodes.Ret);

            return (Func<TArgs, object>)dynamicMethod.CreateDelegate(typeof(Func<TArgs, object>));
        }
    }
}